package searchtab.search.textfile;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;

import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;

import searchtab.highlight.MarkerManager;
import searchtab.search.ISearcher;
import searchtab.search.options.ISearchOptionsListener;
import searchtab.search.options.SearchOptions;
import searchtab.search.options.SearchOptionsChangedEvent;
import searchtab.util.Beeper;
import searchtab.util.SearchTabUtils;
import searchtab.views.SearchTabView;

public class TextEditorSearcher implements ISearcher, ISearchOptionsListener {

    private static final SearcherEvent ONE_RESULT_EVENT = new SearcherEvent(1);

    private static final SearcherEvent ZERO_RESULT_EVENT = new SearcherEvent(0);

    private static final ArrayList<Position> EMPTY_RESULT = new ArrayList<Position>(0);

    private static MarkerManager markers = new MarkerManager();

    private final SearchState searchState = new SearchState();

    private final SearchTabView searchTabView;

    private final HashSet<ISearcherEventListener> listeners = new HashSet<ISearcherEventListener>();

    private final Beeper beeper;

    private IDocument currentDocument;

    public TextEditorSearcher(SearchTabView searchTabView) {
        SearchOptions.reset();
        SearchOptions.getInstance().addListener(this);
        this.searchTabView = searchTabView;
        beeper = new Beeper(searchTabView);
    }

    public void runSearch() {

        searchState.setFilterText(searchTabView.getFilterText());

        if (checkState(true) == false) {
            searchState.setPreviousFilterText(searchState.getFilterText());
            beeper.beepIfRequierd(searchState);
            return;
        }

        if (SearchOptions.isHighlightAll()) {
            searchAndMark(false);
        }

        goToMatch(true, false);

        searchState.setPreviousFilterText(searchState.getFilterText());

    }

    public void markResults() {
        searchState.setFilterText(searchTabView.getFilterText());
        searchAndMark(true);
    }

    private boolean searchAndMark(boolean markOnly) {
        boolean findSmth = searchList();
        // searchState.setPreviousFilterText(searchState.getFilterText());
        if (!findSmth) {
            return false;
        }
        markers.setMarkers(searchState.getAnnotationModel(), searchState.getResult());
        return true;
    }

    public void unmarkResults() {
        markers.deleteMarkers();

    }

    public void goToNextMatch() {
        searchState.setFilterText(searchTabView.getFilterText());
        if (checkState(false) == false) {
            return;
        }
        goToMatch(true, true);

    }

    public void goToPreviousMatch() {
        searchState.setFilterText(searchTabView.getFilterText());
        if (checkState(false) == false) {
            return;
        }
        goToMatch(false, true);
    }

    private boolean searchList() {

        ITextEditor editor = SearchTabUtils.getEditor();
        if (editor == null) {
            fireResultEvent(ZERO_RESULT_EVENT);
            return false;
        }

        IDocumentProvider documentProvider = editor.getDocumentProvider();

        IDocument document = documentProvider.getDocument(editor.getEditorInput());

        String text = document.get();

        searchState.setAnnotationModel(documentProvider.getAnnotationModel(editor.getEditorInput()));

        markers.deleteMarkers();

        StringMatcher stringMatcher = new StringMatcher(searchState.getFilterText(),
                !SearchOptions.isMatchCase(), true);

        searchState.setResult(TextSearcherUtils.findAll(text, stringMatcher, 0));
        searchState.setNoResults(searchState.getResult().size() == 0);

        fireResultEvent(new SearcherEvent(searchState.getResult().size()));

        if (searchState.isNoResults()) {
            return false;
        }

        return true;
    }

    private int searchListNoAnnotations() {

        ITextEditor editor = SearchTabUtils.getEditor();
        if (editor == null) {
            return 0;
        }

        IDocumentProvider documentProvider = editor.getDocumentProvider();

        IDocument document = documentProvider.getDocument(editor.getEditorInput());

        String text = document.get();

        StringMatcher stringMatcher = new StringMatcher(searchState.getFilterText(),
                !SearchOptions.isMatchCase(), true);

        return TextSearcherUtils.findAll(text, stringMatcher, 0).size();
    }

    private void goToMatch(boolean goForward, boolean stepRequired) {

        // find
        int index = TextSearcherUtils.findAndSelect(SearchTabUtils.getFindReplaceTarget(), goForward,
                stepRequired, searchState);

        searchState.setNoResults(index == -1);

        beeper.beepIfRequierd(searchState);

        fireResultEvent(index == -1 ? ZERO_RESULT_EVENT : new SearcherEvent(searchListNoAnnotations()));

        if (SearchOptions.isHighlightAll()) {
            if (SearchTabUtils.getDocument() != currentDocument || searchState.isMarkRequired()) {
                markResults();
                currentDocument = SearchTabUtils.getDocument();
                searchState.setMarkRequired(false);
            }
        }

    }

    private boolean checkState(boolean isTyping) {

        if (searchState.getFilterText().length() == 0) {
            fireResultEvent(new SearcherEvent());
            searchState.setResult(EMPTY_RESULT);
            searchState.setNoResults(false);
            if (SearchOptions.isHighlightAll()) {
                markers.deleteMarkers();
            }
            return false;
        }

        if (isTyping && searchState.isNoResults()) {
            if (searchState.getFilterText().contains(searchState.getPreviousFilterText())) {
                if (SearchOptions.isHighlightAll()) {
                    markers.deleteMarkers();
                }
                fireResultEvent(ZERO_RESULT_EVENT);
                return false;
            }
        }
        return true;
    }

    public void onSearchOptionChanged(SearchOptionsChangedEvent optionsChangedEvent) {
        if (optionsChangedEvent.getOptionName().equals(SearchOptions.MATCH_CASE)) {
            if ((Boolean) optionsChangedEvent.getOptionNewValue() == false) {
                searchState.setNoResults(false);
            }
            searchState.setMarkRequired(true);
        }

    }

    private void fireResultEvent(SearcherEvent resultEvent) {
        Iterator<ISearcherEventListener> iter = listeners.iterator();
        while (iter.hasNext()) {
            iter.next().onGetResultEvent(resultEvent);
        }
    }

    public void addListener(ISearcherEventListener listener) {
        listeners.add(listener);
    }

    public void removeListener(ISearcherEventListener listener) {
        listeners.remove(listener);
    }

    public void removeAllListeners() {
        listeners.clear();
    }

}
